Before I get into part II of my earlier post about Configuring and Debugging WCF Services, I want to write about a problem I solved recently. Took a lot of digging around and so I thought I would save someone time by making this post.
Disclaimer: I am primarily a Windows Developer. I usually don’t have any issues going between platforms, languages and such but by no means am I an expert on the Linux platform (or on SOAP for that matter).
The Motivation
The basic problem I was trying to solve was trying to hit a WCF service from Linux using C++. Why was I doing that you might ask. Well, in most environments out there today, programmers and architects have to make heterogeneous systems work together and deal with legacy systems that will not die (the reasons are many, and complicated).
Service Oriented Architectures come with the promise of making this integration between different platforms easy to achieve, maintain and evolve. However, choosing the technologies to implement SoA in is an important decision, as to be useful, the services you create should be accessible from every platform easily. Only then will you get the buy in needed for SoA to succeed.
gSOAP
gSOAP is a technology that allows you to create stubs for client and server side code from WSDLs. There is a lot more that gSOAP can do but I only used it for creating the client side stubs using the WSDL I got from my WCF service.
You can read all about it here.
Making it Work
OK, let’s get down to how we can use gSOAP to access a WCF service using a C++ client. The steps I had to take were
- Install the gSOAP library on my Linux box, in my local directory
- Generate the SOAP stubs for the target WCF service
- Create the client
Here are the steps again with many more details:
Install the gSOAP library
1. Download the gSOAP tar file. The website is here.
wget http://sourceforge.net/projects/gsoap2/files/gSOAP/2.7.14%20stable%20%28update%29/gsoap_2.7.14.tar.gz/download
2. Untar the file.
tar -xvzf packagename.tar.gz
3. Make and install. Run the following commands.
./configure make // The exec_prefix is to install in your home directory make install exec_prefix=$HOME
Generate the SOAP stubs for the target service.
I installed gSOAP in my home directory. Also lets assume that the service I am trying to connect to exposed an endpoint with BasicHttpBinding, which boils down to simple SOAP. And it is located at the following address
http://www.myserver.com/myWCFservice.svc
and the method I am trying to invoke has the following signature:
// The service implements the IMyService interface // and the interface has the GetCount method on it int IMySevice.GetCount(string inputString)
You would need to execute the following commands to get your SOAP stubs:
Go to the appropriate directory for the platform you are on.
cd /lib/gSOAP/gsoap-2.7/gsoap/bin/linux386
Generate the WSDL header file.
./wsdl2h -o mywcfheader.h http://myserver.com/myWCFService.svc?wsdl
Generate the stub files by executing the gSOAP compiler
./soapcpp2 -I "/lib/gSOAP/gsoap-2.7/gsoap/" mywcfheader.h
At this point you should have the stub files in you directory. As far as the client is concerned, you care about these files
File Name |
Description |
BasicHttpBinding_IMyService.GetCount.req.xml | Example SOAP Request |
BasicHttpBinding_IMyService.GetCount..res.xml | Example SOAP Response |
soapBasicHttpBinding_IMyServiceProxy.h | C++ Proxy that wraps the SOAP calls that are made on the client side into an Object Oriented interface. The client you write will consume this. |
BasicHttpBinding_IMyService.nsmap | File that defines the name space (for versioning of the SOAP protocol) of various schema prefixes |
soapC.cpp, soapClient.cpp, soapClientLib.cpp, soapH.h and soapStub.h | Client Side SOAP Code that the C++ proxy consumes |
Create the client.
The client code is pretty simple. It looks something like this:
// This will be the name of the proxy file created when // you generate the stubs #include “soapBasicHttpBinding_IMyServiceProxy.h” // This will be the nsmap file created when you created // the stubs #include “BasicHttpBinding_IMyService.nsmap” using namespace std; int main() { // This will be the name of the service // class in the proxy header file from above BasicHttpBinding_IMyService s; // This is the request and response that // the service you are trying to call takes. // Again you can find the types in the class // used in the C++ proxy header _ns1__GetCount req; _ns1__GetCountResponse resp; string is(”Hello There America”); req.inputString = &is; int err = s.__ns1__GetCount(&req, &resp); if (SOAP_OK == err) cout << “Service Returned: ” << *resp.GetCountResult << endl; else cout << “Error: ” << err << endl; return 0; }
Since our binding is BasicHttpBinding, the WCF service expects SOAP 1.1 as the protocol. By default gSOAP 2.7 talks in SOAP 1.2. So we need to make sure that we make changes required to generate a client that will communicate using SOAP 1.1.
We can do this by changing the following in your *.nsmap and C++ Proxy header file.
Change:
{“SOAP-ENV”, “http://www.w3.org/2003/05/soap-envelope”, “http://www.w3.org/2003/05/soap-envelope”, NULL}, {“SOAP-ENC”, “http://www.w3.org/2003/05/soap-encoding”, “http://www.w3.org/2003/05/soap-encoding”, NULL}, {“xsi”, “http://www.w3.org/2001/XMLSchema-instance”, “http://www.w3.org/*/XMLSchema-instance”, NULL}, {“xsd”, “http://www.w3.org/2001/XMLSchema”, “http://www.w3.org/*/XMLSchema”, NULL}
to
{“SOAP-ENV”, “http://schemas.xmlsoap.org/soap/envelope/”, NULL, NULL}, {“SOAP-ENC”, “http://schemas.xmlsoap.org/soap/encoding/”, NULL, NULL}, {“xsi”, “http://www.w3.org/2001/XMLSchema-instance”, NULL, NULL}, {“xsd”, “http://www.w3.org/2001/XMLSchema”, NULL, NULL}
I found this information here.
Compile the client
Now we are ready to compile the client. Besides the client code you write and the stub files generated by gSOAP, we also need to supply the compiler with another file stdsoap2.cpp that comes with the installation. Also we need to add the gSOAP lib path to the include path so that it can pick up the appropriate libraries it needs.
The command will look like this:
g++ -I "/lib/gSOAP/gsoap-2.7/gsoap" myclient.cpp soapC.cpp soapClient.cpp /lib/gSOAP/gsoap-2.7/gsoap/stdsoap2.cpp
At this point things should work. Leave comments if something does not work for you or if there are other ways to do this.
Ahoy, this is some useful information.
Thanks a lot and keep up the good work!
When I run wsdl2h : (wsdl2h -i -s -o myfile.h http://urltothe:port/?wsdl) it runs fine and then I run soapcpp2 -i -C myfile.h I do not get a soapClient.cpp nor the soapClientLib.cpp, but do get the soapC.cpp, stdsoap2.h proxy.h the nsmap and the other files, but no soapClient.cpp or soapClientLib.cpp. Is there a bug in 2.7.16 or am I doing something wrong?
you have to run the soapcpp2 by eliminating the -i and -C options. Just by simply saying like this
soapcpp2 myfile.h this will generate all the missing files that you are looking for. I think the reply is too late but still answering it because someone might be looking for this.
Hi, FYI, the change needs to be made in the Proxy CPP, not header file.
Thanks!
You can add the parameter ‘-1’ to the soapcpp2 call, it will generate SOAP1.1 bindings; then you don’t have to change the proxy file by hand.
When I run wsdl2h I get a ‘Floating point exception’.
The URL of the service loads fine in browser. Ping from console to the hosting machine also works so it is not a proxy setting made only on browser.
Can anyone help me?
./wsdl2h -t /usr/local/share/gsoap/WS/typemap.dat -o aaa.h “http://xxx:8087/yyyService.svc?wsdl”
** The gSOAP WSDL/Schema processor for C and C++, wsdl2h release 2.8.6
** Copyright (C) 2000-2011 Robert van Engelen, Genivia Inc.
** All Rights Reserved. This product is provided “as is”, without any warranty.
** The wsdl2h tool is released under one of the following two licenses:
** GPL or the commercial license by Genivia Inc. Use option -l for details.
Saving aaa.h
Reading type definitions from type map file ‘/usr/local/share/gsoap/WS/typemap.dat’
Connecting to ‘http://xxx:8087/yyyService.svc?wsdl’ to retrieve WSDL/XSD…
Floating point exception
Can you tell me how the WSDL file that you mentioned above was generated. Did you use WCF (C#) web services or you have written it?
I test in C++ ==> Run OK !!!!
But I want to build Client use C programming ?
struct soap soap; // the gSOAP runtime context
struct _ns3__HelloWorld _ns3__HelloWorld;
struct _ns3__HelloWorldResponse _ns3__HelloWorldResponse;
memset( &_ns3__HelloWorld, 0, sizeof( struct _ns3__HelloWorld ));
memset( &_ns3__HelloWorldResponse, 0, sizeof( struct _ns3__HelloWorldResponse ));
soap_init(&soap); // initialize the context (only once!)
_ns3__HelloWorld.msg = soap_strdup(&soap,”aaaaaac”);
soap_set_namespaces( &soap, a_namespaces );// set Name Space
if (soap_call___ns1__HelloWorld(&soap,”http://192.168.1.2/QuanLyNoCuoc”,NULL, &_ns3__HelloWorld, &_ns3__HelloWorldResponse) == SOAP_OK){
printf(“kq=%s”,_ns3__HelloWorldResponse.HelloWorldResult);
}
else // an error occurred
soap_print_fault(&soap, stderr); // display the SOAP fault message on the stderr stream*/
I have error:
Error 400 fault: SOAP-ENV:Server [no subcode]
“HTTP Error”
Detail: HTTP/1.1 400 Bad Request
wsdl2h -c -o test.h http://192.168.1.2/QuanLyNoCuoc?wsdl
soapcpp2 test.h
I know that when I send by C program, header not same as header in WCF
What mus I do ?
Hi , i am trying to add security header to the client.WS-security.username token and Timestamp, have you got any idea on how to do it..
Thank you
i have build basic (very basic) WCF client-server application, which works fine. when i try command
.\wsdl2h.exe -o myheader.h “http://localhost:5000/service?wsdl” it reports an Error 400 fault: SOAP-ENV:Server [no subcode]. i have tried it on linux (compiled thru make) and winxp (used precompiled exe). whats wrong?
That’s a good one.
One thing I would like to ask you, have you ever test WCF wsHttpBinding in the linux. Becasue as far as I know gsoap won’t support the wsHttpBinding.